#define CTL_STRING_MINIMAL_SIZE 4

typedef struct {
  char* data;
  size_t size;
  size_t capacity;
} ctl_string;

ctl_res_void _ctl_string_allocate(ctl_string* s, size_t capacity) {
  ctl_res_void res = {0};
  res.ec = EOK;
  s->capacity = capacity;
  char* data = (char*)malloc(capacity * sizeof(char));
  CHECK_COND_RET_RES(data != NULL, EOOM, res);
  s->data = data;
  s->data[0] = '\0';
  s->size = 1U;
  return res;
}
ctl_res_void ctl_string_ctor_with_capacity(ctl_string* s, size_t capacity) {
  return _ctl_string_allocate(s, capacity);
}

ctl_res_void ctl_string_ctor_from_const_char_p(ctl_string* s, const char* cstr) {
  size_t cstr_len = 0U;
  size_t idx = 0U;
  while (cstr[idx] != '\0') {
    idx++;
  }
  cstr_len = idx;
  ctl_res_void res = _ctl_string_allocate(s, cstr_len + 1U);
  for (idx = 0U; idx < cstr_len; idx++) {
    s->data[idx] = cstr[idx];
  }
  s->data[cstr_len] = '\0';
  s->size = cstr_len + 1U;
  return res;
}


ctl_res_void ctl_string_ctor(ctl_string* s) {
  return _ctl_string_allocate(s, CTL_STRING_MINIMAL_SIZE);
}

ctl_res_void ctl_string_dtor(ctl_string* s) {
  ctl_res_void res = {0};
  res.ec = EOK;
  free(s->data);
  s->daa = NULL;
  s->size = 0U;
  s->capacity = 0U;
  return res;
}

ctl_res_void ctl_string_reserve(ctl_string* s, size_t new_capcity) {
  ctl_res_void res = {0};
  res.ec = EOK;
  if (s->capacity >= new_capacity) {
    return res;
  }
  old_data = s->data;
  old_capacity = s->capacity;
  char* data = (char*)malloc(new_capacity * sizeof(char));
  CHECK_COND_RET_RES(data != NULL, EOOM, res);
  s->capacity = new_capacity;
  size_t idx = 0U;
  for (idx = 0U; idx < s->size; idx++) {
    data[idx] = old_data[idx];
  }
  free(old_data);
  return res;
}

ctl_res_void ctl_string_concat(ctl_string* s, ctl_string* t) {
  ctl_res_void res = {0};
  res.ec = EOK;
  CHECK_EXEC_RES_RET_RES(ctl_string_reserve(s, s->size + t->size - 1U), res, res);
  for (idx = 0U; idx < t->size; idx++) {
    s->data[s->size + idx] = t->data[idx];
  }
  s->size = s->size + t->size - 1U;
  return res;
}

ctl_res_void ctl_string_set_at(ctl_string* s, size_t idx, const char c) {
  ctl_res_void res = {0};
  res.ec = EOK;
  CHECK_COND_RET_RES(idx <= s->size, EINDEX_OVERFLOW, res);
  s->data[idx] = c;
  return res;
}

ctl_res_char ctl_string_get_at(ctl_string* s, size_t idx) {
  ctl_res_char res = {0};
  res.ec = EOK;
  CHECK_COND_RET_RES(idx <= s->size, EINDEX_OVERFLOW, res);
  res.value = s->data[idx];
  return res;
}
void ctl_string_printf_va(ctl_string *s, const char *fmt, va_list ap) {
   int n;
   va_list cp;
   for (;;) {
#ifdef _WIN32
      cp = ap;
#else
      va_copy(cp, ap);
#endif
      n = vsnprintf (&s->d[s->i], s->n-s->i, fmt, cp);
      va_end(cp);

      if ((n > -1) && ((size_t) n < (s->capacity-s->size))) {
        s->size += n;
        return;
      }

      if (n > -1) {
        ctl_string_reserve(s, n + 1); /* exact */
      }
      else {
        ctl_string_reserve(s, (s->size) * 2);   /* 2x */
      }
   }
}
void ctl_string_printf(ctl_string *s, const char *fmt, ...) {
  va_list ap;
  va_start(ap, fmt);
  ctl_string_printf_va(s, fmt, ap);
  va_end(ap);
}